<?php
/**
 * Created by PhpStorm.
 * User: 65736
 * Date: 2019/4/4 0004
 * Time: 17:15
 */

namespace app\common\service;


use app\common\library\CommonFun;
use app\common\library\ImageKuPHP;
use OSS\Core\OssException;
use OSS\OssClient;
use think\Config;
use think\Log;

class Storage
{
    /**
     * oss客户端连接句柄
     * @var OssClient
     */
    private $client;

    /**
     * 错误信息
     * @var string
     */
    private $error = '';

    /**
     * oss容器
     * @var string
     */
    private $bucket = '';

    /**
     * 主域
     * @var string
     */
    private $domain = '';

    /**
     * url
     * @var string
     */
    public $url_prifix = '';

    /**
     * 构造函数
     * Storage constructor.
     * @throws \think\Exception
     */
    public function __construct()
    {
        // 加载配置
        CommonFun::get_other_config("oss_config", 2);
        $OssConfig = Config::get("oss_config");
        if (!$OssConfig) CommonFun::make_exception("Oss config not found");
        $accessKeyId = $OssConfig['access_key_id'];
        $accessKeySecret = $OssConfig['access_key_secret'];
        $endPoint = $OssConfig['end_point'];
        $this->bucket = $OssConfig['bucket'];
        $this->domain = $OssConfig['domain'];
        $this->url_prifix = $OssConfig['url_prefix'];
        try {
            $this->client = new OssClient($accessKeyId, $accessKeySecret, $endPoint);
        } catch (OssException $e) {
            CommonFun::make_exception("Storage Client Error:" . $e->getErrorMessage());
        }
    }

    /**
     * 保存资源到阿里云OSS
     * @param $objectKey
     * @param $filePath
     * @throws \think\Exception
     */
    public function save_resource($objectKey, $filePath)
    {
        if (!$objectKey) {
            CommonFun::make_exception("保存资源到阿里云OSS失败![Key错误]");
        }
        if (!file_exists($filePath)) {
            CommonFun::make_exception("保存资源到阿里云OSS失败![源文件不存在]");
        }
        try {
            $this->client->uploadFile($this->bucket, $objectKey, $filePath);
        } catch (OssException $e) {
            @unlink($filePath);
            CommonFun::make_exception("Storage Upload Error:" . $e->getErrorMessage());
        }
    }

    /**
     * 删除资源
     * @param $objectKey
     * @throws \think\Exception
     */
    public function delete_resource($objectKey)
    {
        if (!$objectKey) {
            CommonFun::make_exception("从阿里云OSS删除资源失败![Key错误]");
        }
        $this->client->deleteObject($this->bucket, $objectKey);
    }

    /**
     * 生成路径信息
     * @param string $suffix 文件后缀名
     * @return array|bool
     */
    public function generate_upload_filepath($suffix)
    {
        // 文件夹名称
        $folder_name = "PHP_UPLOAD/" . date("Ymd");
        // 生成文件名
        $filename = md5(uniqid());
        // 散列文件夹
        $filename_folder = substr($filename, 0, 4) . '/';
        // 虚拟路径
        $virtual_path = $folder_name . '/' . $filename_folder;
        // 绝对路径
        $absolute_path = WEB_UPLOADS . 'oss_tmp/' . $virtual_path;
        // 创建文件夹
        !is_dir($absolute_path) && $res = mkdir($absolute_path, 0777, true) && chmod($absolute_path, 0777);
        if (isset($res) && !$res) {
            return false;
        }

        // 拼接文件名
        $filename = $filename . '_' . $suffix;
        $virtual_path = $virtual_path . $filename;
        $absolute_path = $absolute_path . $filename;
        $info = array(
            'filename' => $filename,
            'virtual_path' => $virtual_path,
            'absolute_path' => $absolute_path,
        );
        return $info;
    }

    /**
     * 上传图片到OSS
     * @param string $img_field    图片表单字段
     * @param string $image_config_type 图片配置类型名称
     * @return array
     * @throws \think\Exception
     * @throws \ImagickException
     */
    public function upload_img($img_field, $image_config_type = '')
    {
        // 初始检测
        if (!isset($_FILES[$img_field]) || !isset($_FILES[$img_field]['tmp_name'])) {
            // 图片上传失败
            $this->setError('图片上传失败[$_FILES不存在]');
        }
        if ($_FILES[$img_field]['error'] != UPLOAD_ERR_OK) {
            $this->setError('图片上传失败[Error:' . $_FILES[$img_field]['error'] . ']');
        }
        if ($this->getError()) {
            Log::error($this->getError());
            return array('info' => $this->getError(), 'status' => 0, 'data' => array());
        }

        // 读取原始名，并判断扩展名
        $file_img = $_FILES[$img_field];
        $ori_name = $file_img['name'];
        $ori_size = $file_img['size'] / (1024 * 1024);
        $tmp_file = $file_img['tmp_name'];
        $ori_type = $file_img['type'];
        //$base_name = basename($tmp_file);
        $ori_ext = strtolower(trim(strrchr($ori_name, '.'), '.'));
        if (!in_array($ori_ext, array('jpg', 'gif', 'png', 'jpeg', 'webp')) || !in_array($ori_type, array('image/jpeg', 'image/png', 'image/gif', 'image/webp'))) {
            // 图片格式不正确
            Log::error($error = '图片上传失败[格式不正确' . $ori_ext . ']');
            $this->setError($error);
            @unlink($tmp_file);
            return array('info' => $error, 'status' => 0, 'data' => array());
        }
        // 图片配置类型
        if ($image_config_type) {
            // 获取图片配置
            $image_config = CommonFun::get_image_config($image_config_type);
            if (!$image_config) {
                // 没有图片尺寸配置
                Log::error($error = '图片上传失败[没有图片配置选项]');
                @unlink($tmp_file);
                return array('info' => $error, 'status' => 0, 'data' => array());
            }
            // 图片配置尺寸
            $image_config_size = $image_config['size'];
            // 图片配置比例
            $image_config_ratio = $image_config['ratio'];
            // 图片配置容量
            $image_config_memory = $image_config['memory'];
            if ($ori_size > $image_config_memory) {
                // 图片大小大于配置大小
                Log::error($error = '图片上传失败[图片不能大于' . $image_config_memory . 'M]');
                @unlink($tmp_file);
                return array('info' => $error, 'status' => 0, 'data' => array());
            }

            // 获取图片尺寸
            if ( $ori_ext == "webp" ) {
                //图片处理类
				$Imagick = new \Imagick();
                $image_size = array(
                    $Imagick->getimagewidth(),
                    $Imagick->getimageheight()
                );
            }else {
                $image_size = getimagesize($tmp_file);
            }

            if (!$image_size[0] || !$image_size[1]) {
                // 没拿到图片尺寸
                Log::error($error = '图片上传失败[未获取到图片尺寸]');
                @unlink($tmp_file);
                return array('info' => $error, 'status' => 0, 'data' => array());
            }
            // 图片宽高
            list($img_width, $img_height) = array($image_size[0], $image_size[1]);
            // 获取上传图片宽高最大公约数
            $divisor = CommonFun::max_divisor_with_two_nums($img_width, $img_height);
            // 图片比例
            $img_ratio = $img_width / $divisor . ":" . $img_height / $divisor;
            // 计算比例是否合法
            if ($image_config_ratio != $img_ratio && $image_config_ratio != '0:0') {
                // 图片比例非法
                Log::error($error = '图片上传失败[图片比例应该为' . $image_config_ratio . ']');
                @unlink($tmp_file);
                return array('info' => $error, 'status' => 0, 'data' => array());
            }

        } else {

            // 获取图片尺寸
            if ( $ori_ext == "webp" ) {
                //图片处理类
                $Imagick = new \Imagick($tmp_file);
                $image_size = array(
                    $Imagick->getimagewidth(),
                    $Imagick->getimageheight()
                );
            }else {
                $image_size = getimagesize($tmp_file);
            }

            if (!$image_size[0] || !$image_size[1]) {
                // 没拿到图片尺寸
                Log::error($error = '图片上传失败[未获取到图片尺寸]');
                @unlink($tmp_file);
                return array('info' => $error, 'status' => 0, 'data' => array());
            }
            // 图片宽高
            list($img_width, $img_height) = array($image_size[0], $image_size[1]);
            $image_config_size['w'] = $img_width;
            $image_config_size['h'] = $img_height;
        }

        // 生成路径信息
        $suffix = $image_config_size['w'].'x'.$image_config_size['h'].'.'.$ori_ext;
        $path_info = $this->generate_upload_filepath($suffix);
        $virtual_path = $path_info['virtual_path'];
        //$virtual_name = $path_info['filename'];
        $absolute_path = $path_info['absolute_path'];
        if ( $ori_ext == "webp" ) {
            //图片处理类
            $Imagick = new \Imagick($tmp_file);
            // 设置图片类型
            $Imagick->setimagetype($ori_ext);

            // 保存图片
            $resize_link = $Imagick->writeimage($absolute_path);
        }else {
            // 生成图片
            $config = array(
                'source_image' => $tmp_file,
                'create_thumb' => true,
                'maintain_ratio' => true,
                'thumb_marker' => '',
                'width' => $image_config_size['w'],
                'height' => $image_config_size['h'],
                'new_image' => $absolute_path,
            );
            $img_class = new ImageKuPHP($config);
            $resize_link = $img_class->resize();
        }

        if (!$resize_link) {
            // 处理缩略图失败
            Log::error($error = '处理图片失败');
            @unlink($tmp_file);
            @unlink($absolute_path);
            return array('info'=>$error,'status'=>0, 'data'=>array());
        }
        if (!is_file($absolute_path)) {
            // 没有生成新尺寸图片
            Log::error($error = '没有生成新尺寸图片');
            @unlink($tmp_file);
            @unlink($absolute_path);
            return array('info'=>$error,'status'=>0, 'data'=>array());
        }

        // 上传OSS
        $this->save_resource($virtual_path, $absolute_path);
        // 图片Url域名地址
        $img_source_url = $this->url_prifix;
        $result = array(
            'virtual_path' => $virtual_path,
            'absolute_path' => $absolute_path,
            'preview_path' => $img_source_url.$virtual_path
        );
        @unlink($tmp_file);
        @unlink($absolute_path);
        return array('info'=>'上传成功','status'=>1, 'data'=>$result);
    }


    /**
     * @return string
     */
    public function getError(): string
    {
        return $this->error;
    }

    /**
     * @param string $error
     */
    public function setError(string $error)
    {
        $this->error = $error;
    }


}